package edu.bucknell.edash;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Handler;
import android.util.AttributeSet;
//import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;


/**
 * @author abb011
 * Class that Draws Gauges and handles there animation based on the specified fields in the XML files
 */
public class Gauge extends ImageView {
	private Bitmap backgroundIm;
	private Bitmap background;
	private Bitmap needle;
	private Handler h;
	private float angle = 0;
	private double scale;
	private float velocity;
	private float destination = 0;
	private float minAngle =0;
	private float maxAngle = 180;
	private float minVal = 0;
	private float maxVal = 100;


	
	private final static int FRAME_RATE = 30;
	// How many frames the needle should reach its destination within
	private static final float TICKS_TO_MOVE =(float) 5;
	//How close the angles need to be to be considered the same (in degrees)
	private static final float EPSILON = (float) 1;

	// Forces the display to be redrawn when this is run
	private Runnable r = new Runnable() {
		@Override
		public void run() {
			invalidate();
		}

	};
	
	/**
	 * Default Constructor
	 * @param context
	 */
	public Gauge(Context context) {
		super(context);
		h = new Handler();
	}

	/**
	 * Constructor that is used if there is XML specification for the Guage
	 * @param context
	 * @param attrs
	 */
	public Gauge(Context context, AttributeSet attrs) {
		super(context, attrs);
		h = new Handler();
		init(context, attrs);
	}
	
	/**
	 * Constructor used if there is XML specification for the guague including defStyle
	 * Un-implimented
	 * 
	 * @param context
	 * @param attrs
	 * @param defStyle
	 */
	public Gauge(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		h = new Handler();
	}

	/**
	 * Initialize the Gauge based on the XML specification This is dependent on attrs.xml
	 * @param context the context of the fragment
	 * @param attrs the attributes of the item
	 */
	private void init(Context context, AttributeSet attrs) {
		System.out.println("In Innit");
		TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.Gauge);
		
		float minAngle=-1, maxAngle=-1, minValue=-1, maxValue =-1;
		
		Bitmap backgroundBMP = BitmapFactory.decodeResource(this.getResources(),R.drawable.ic_launcher);
		Bitmap needleBMP = BitmapFactory.decodeResource(this.getResources(),R.drawable.ic_launcher);
		System.out.println(backgroundBMP + "\n Needle\n"+needleBMP);
		final int N = a.getIndexCount();
		System.out.println("Number of attributes "+N);
		
		// Walking Across all elements to initialize them all in the GUI
		for (int i = 0; i < N; ++i){
			int attr = a.getIndex(i);
			
			switch (attr){
			case R.styleable.Gauge_background:
				int backgroundID = a.getResourceId(attr, -1);

				if(backgroundID == -1){
					System.out.println("Image Not found");

				}else{
					backgroundBMP = BitmapFactory.decodeResource(this.getResources(),backgroundID);
				}
				break;
			case R.styleable.Gauge_needle:
				int needleID = a.getResourceId(attr, -1);
				
				if(needleID ==-1){
					System.out.println("Needle not found");
				}else{
					needleBMP = BitmapFactory.decodeResource(this.getResources(),needleID);
				}
				
				break;
			case R.styleable.Gauge_minAngle:
				minAngle = a.getFloat(attr, 0);
				break;
			case R.styleable.Gauge_maxAngle:
				maxAngle=  a.getFloat(attr, 180);
				break;
			case R.styleable.Gauge_minValue:
				minValue = a.getFloat(attr, 0);
				break;
			case R.styleable.Gauge_maxValue:
				maxValue = a.getFloat(attr, 50);
				break;
			default:
				break;
			}
		}
		this.setBounds(minAngle, maxAngle, minValue, maxValue);
		this.setBackground(backgroundBMP);
		this.setNeedle(needleBMP);
		
		a.recycle();



	}


	/**
	 * Initializes the bounds in both degrees and displayed values for the Gauge
	 * @param minAngle
	 * @param maxAngle
	 * @param minValue
	 * @param maxValue
	 */
	public void setBounds(float minAngle, float maxAngle, float minValue, float maxValue){
		this.minAngle = minAngle;
		this.maxAngle = maxAngle;
		this.minVal = minValue;
		this.maxVal = maxValue;

		this.setTargetVal(minValue);
	}

	/**
	 * Move the needle to the specified value If its out of bounds clamp to those bounds
	 * @param value
	 */
	public void setTargetVal(float value){		
		float angle = (value-minVal) *(((maxAngle-minAngle)/(maxVal-minVal))) +minAngle;
		if(value > this.maxVal)
			angle = this.maxAngle;
		else if(value<this.minVal)
			angle = this.minAngle;
		setNeedleDestination(angle);

	}

	/**
	 * Move the needle to the target angle
	 * @param angle
	 */
	public void setNeedleDestination(float angle){
		destination = angle;
		velocity = (destination-this.angle)/TICKS_TO_MOVE;
	}

	/**
	 * Updating the needle tilt angle
	 * @param angle
	 */
	private void setNeedleAngle(){
		if(!aproxEquals(destination, angle))
			this.angle += velocity;
	}


	/**
	 * Overridden on draw call. This call is made whenever the GUI is to be updated
	 */
	protected void onDraw(Canvas canvas){
		drawNeedle(canvas);
		ViewGroup.LayoutParams layouts = this.getLayoutParams();
		this.scale = ((double)layouts.width)/this.background.getWidth();
		//Delay so that we update at the specified Frame Rate
		h.postDelayed(r, FRAME_RATE);
	}

	/**
	 * Draw the needle on the screen at the correct angle
	 * @param tempCan
	 */
	private void drawNeedle(Canvas tempCan){
		setNeedleAngle();
		tempCan.scale((float)scale, (float)scale);
		tempCan.drawBitmap(backgroundIm, 0, 0, null);
		tempCan.rotate(angle,background.getWidth()/2,background.getHeight()/2);
		tempCan.drawBitmap(needle, 0, 0, null);
		tempCan.rotate(-angle,background.getWidth()/2,background.getHeight()/2);
	}

	
	/**
	 * Set the guagues needle to the needle bitmap provided
	 * @param needle
	 */
	public void setNeedle(final Bitmap needle){
		this.needle = needle;


	}
	
	/**
	 * Set the Background image to the specified background image
	 * @param backgr
	 */
	private void setBGround(final Bitmap backgr){
		System.out.println("In the setting of background");
		this.scale = 1;
		if(backgr.getHeight() == 200){
			this.scale = 2;
		}
		System.out.println("In before getting layout");
		this.backgroundIm = backgr;
		this.background =Bitmap.createBitmap(backgr.getWidth(), backgr.getHeight(), Bitmap.Config.ARGB_8888);

		
	}
	
	/**
	 * Returns the maximum allowed value
	 * @return maxVal
	 */
	public float getMaxVal(){
		return this.maxVal;
	}
	
	/**
	 * Public version of setBGround()
	 * @param backgr
	 */
	public void setBackground(final Bitmap backgr){
		this.setBGround(backgr);
	}

	/**
	 * Evaluates if two floating point numbers are aproximately equal based on EPSILON
	 * @param f1
	 * @param f2
	 * @return true If abs(F1-F2) < Epsilon
	 */
	boolean aproxEquals(float f1, float f2){
		if (Math.abs(f1-f2)>EPSILON)
			return false;
		return true;
	}


}

